function Roti() {
  //dereived from V 1.00.A Beta
  //http://www.openjs.com/scripts/dom/css_selector/
  this.$ = function (all_selectors, root) {
    var selected = new Array();
    if(!document.getElementsByTagName) return selected;
    all_selectors = all_selectors.replace(/\s*([^\w])\s*/g,"$1");//Remove the 'beutification' spaces
    var selectors = all_selectors.split(",");
    // Grab all of the tagName elements within current context	
    var getElements = function(context,tag) {
      if (!tag) tag = '*';
      // Get elements matching tag, filter them for class selector
      var found = new Array;
      for (var a=0,len=context.length; con=context[a],a<len; a++) {
        var eles;
        if (tag == '*') eles = con.all ? con.all : con.getElementsByTagName("*");
        else eles = con.getElementsByTagName(tag);
        
        for(var b=0,leng=eles.length;b<leng; b++) found.push(eles[b]);
      }
      return found;
    }

    COMMA:
    for(var i=0,len1=selectors.length; selector=selectors[i],i<len1; i++) {
      if (! root) root = document;
      var context = new Array(root);
      var inheriters = selector.split(" ");
      
      SPACE:
      for(var j=0,len2=inheriters.length; element=inheriters[j],j<len2;j++) {
        //This part is to make sure that it is not part of a CSS3 Selector
        var left_bracket = element.indexOf("[");
        var right_bracket = element.indexOf("]");
        var pos = element.indexOf("#");//ID
        if(pos+1 && !(pos>left_bracket&&pos<right_bracket)) {
          var parts = element.split("#");
          var tag = parts[0];
          var id = parts[1];
          var ele = document.getElementById(id);
          if(!ele || (tag && ele.nodeName.toLowerCase() != tag)) { //Specified element not found
            continue COMMA;
          }
          context = new Array(ele);
          continue SPACE;
        }

        pos = element.indexOf(".");//Class
        if(pos+1 && !(pos>left_bracket&&pos<right_bracket)) {
          var parts = element.split('.');
          var tag = parts[0];
          var class_name = parts[1];

          var found = getElements(context,tag);
          context = new Array;
          for (var l=0,len=found.length; fnd=found[l],l<len; l++) {
            if(fnd.className && fnd.className.match(new RegExp('(^|\s)'+class_name+'(\s|$)'))) context.push(fnd);
          }
          continue SPACE;
        }

        if(element.indexOf('[')+1) {//If the char '[' appears, that means it needs CSS 3 parsing
          // Code to deal with attribute selectors
          if (element.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?['"]?([^\]'"]*)['"]?\]$/)) {
            var tag = RegExp.$1;
            var attr = RegExp.$2;
            var operator = RegExp.$3;
            var value = RegExp.$4;
	    console.log(context);
          }
          var found = getElements(context,tag);
          context = new Array;
          for (var l=0,len=found.length; fnd=found[l],l<len; l++) {
            if(operator=='=' && fnd.getAttribute(attr) != value) continue;
            if(operator=='~' && !fnd.getAttribute(attr).match(new RegExp('(^|\\s)'+value+'(\\s|$)'))) continue;
            if(operator=='|' && !fnd.getAttribute(attr).match(new RegExp('^'+value+'-?'))) continue;
            if(operator=='^' && fnd.getAttribute(attr).indexOf(value)!=0) continue;
            if(operator=='$' && fnd.getAttribute(attr).lastIndexOf(value)!=(fnd.getAttribute(attr).length-value.length)) continue;
            if(operator=='*' && !(fnd.getAttribute(attr).indexOf(value)+1)) continue;
            else if(!fnd.getAttribute(attr)) continue;
            context.push(fnd);
          }

          continue SPACE;
        }

        //Tag selectors - no class or id specified.
        var found = getElements(context,element);
        context = found;
      }
      for (var o=0,len=context.length;o<len; o++) selected.push(context[o]);
    }
    return selected;
  };

  this.spec = {};

  this.process =  function() {
    var links = this.findRotilinks();
    for (var i = 0; i < links.length; i++) {
      var rotilink = links[i];
      console.log(rotilink);
      var root = this.getEntryRoot(rotilink);
      console.log(root);
      var title = this.getTitle(root);
      console.log(title);
      var tags = this.getTags(root);
      console.log(tags);
      var link = this.getLink(root);
      console.log(link);
      var body = this.getBody(root);
      body = body.innerHTML.replace(/<[^<]*>/g, '').replace(/\s+/g, ' ').replace(/&nbsp;/g, '').substr(0,300);
      console.log(body);
      var url = this.genRequestUrl(link, title, tags, body);      
      console.log(url);
      //rotilink.innerHTML = "<b>" + url + "</b>"; 
      this.callSearch(rotilink, url);
    }
  };
  this.genRequestUrl = function(link, title, tags, body) {
    var tmp = [];
    //tmp.push('http://roti.me:8080/roti/search/jsonp');
    tmp.push('http://localhost:8080/roti/search/jsonp');
    tmp.push('?url=');
    tmp.push(encodeURI(link));
    if (tags.length > 0) {
      tmp.push('&tags=');
      tmp.push(encodeURI(tags.join(',')));
    }
    if (title) {
      tmp.push('&title=');
      tmp.push(encodeURI(title));
    }
    tmp.push('&content=');
    tmp.push(encodeURI(body));

    return tmp.join('');    
  }
  this.findRotilinks = function() {
    return this.$('div.rotilinks');
  }
  // find parent node that has class property that match given string
  this.findParent = function(className, elm) {
    var re = new RegExp("(^|\\s)" + className + "(\\s|$)");
    do {
      elm = elm.parentNode;
    } while (elm && elm.nodeType == 1 && ! re.test(elm.getAttribute('class')));
    return elm;
  }

  this.getEntryRoot = function(elm) {
    return this.findParent(this.spec.root, elm);
  }

  this.genericFind = function(root, spec) {
    for (var i=0; i<spec.length; i++) {
      var tmp = this.$(spec[i], root);
      if (tmp.length > 0) {
        return tmp;
      }
    }
    return [null];
  }

  this.getTitle = function(root) {
    var t = this.genericFind(root, this.spec.title)[0];
    if (t != null)
        return t.innerHTML;
    else
        return '';
  }

  this.getBody = function(root) {
    var b = this.genericFind(root, this.spec.body)[0];
    
    if (b != null)
        return b;
    else
        return document.createElement('p');
  }

  this.getTags = function(root) {
    var tags = this.genericFind(root, this.spec.tags)
    console.log(tags);
    var ret = [];
    for (var i = 0; i < tags.length; i++) {
      if (tags[i] && tags[i].innerHTML.search(/uncategorize/i) >= 0) ret.push(tags[i].innerHTML);
    }
    return ret;
  }

  this.getLink = function(root) {
    var tmp = this.genericFind(root, this.spec.link)
    if (tmp[0]) {
       return tmp[0].href; 
    }
    return document.location;
  }

  this.cnt = 0;

  this.callSearch = function(elm, url) {
      var callback = this.genCallBack(elm);
      url = url + '&callback=' + callback;
      var script = document.createElement('script');
      script.setAttribute('src', url);
      script.setAttribute('type', "text/javascript");
      document.body.appendChild(script);
  }

  this.genCallBack = function(elm) {
      var callback = function(result) {
          _roti.render(elm, result.data);
      }
      var name = '_roti_callback' + this.cnt;
      this.cnt++;
      window[name] = callback;
      return name;
  }

  this.render = function(elm, entries) {
      if (elm != null && entries != null) {
        var len = entries.length;

        if (len > 0) {
          var ul = document.createElement('ul');
          for (var i=0; i<len; i++) {
              var li = document.createElement('li');
              var a = document.createElement('a');
              a.setAttribute('href',entries[i].link);
              a.innerHTML = entries[i].title;
              li.appendChild(a);
              ul.appendChild(li);
          }
          elm.appendChild(ul);
        } else {
          var p = document.createElement('p');
          p.innerHTML = 'no content found';
          elm.appendChild(p);
        }
      } else if (elm != null) {
          var pp = document.createElement('p');
          pp.innerHTML = 'no content found';
          elm.appendChild(pp);              
      }
  }
}

function RotiBlogspot()  {
  this.name = 'blogspot';
  this.spec.root = 'post';
  this.spec.title = ['*.post-title a'];
  this.spec.body = ["div.post-body"];
  this.spec.tags = ["a[rel='tag']"];
  this.spec.link = ["a.timestamp-link"];
}

function RotiWordpress()  {
  this.name = 'wordpress';
  this.spec.root = 'post';
  this.spec.title = ["a[rel='bookmark']"];
  this.spec.body = ['div.entry'];
  this.spec.tags = ["a[rel='category','*.postmetadata a']"];
  this.spec.link = ["a[rel='bookmark']"];
} 

function RotiExteen() {
  this.name = 'exteen';
  this.spec.root = 'entry';
  this.spec.title = ['div.title a'];
  this.spec.body = ['div.post'];
  this.spec.tags = ['span.tag a'];
  this.spec.link = ['div.title a'];
}

function RotiDrupal() {
  this.name = 'drupal';
  this.spec.root = 'node';
  this.spec.title = ["a[rel='bookmark', '*.title a']"];
  this.spec.body = ['div.entry-content', 'div.content'];
  this.spec.tags = ["a[rel='tag']"];
  this.spec.link = ["a[rel='bookmark', '*.title a']"];
}

_roti = new Roti();
RotiBlogspot.prototype = _roti;
RotiWordpress.prototype = _roti;
RotiExteen.prototype = _roti;
RotiDrupal.prototype = _roti;

_roti.old_onload = window.onload;
window.onload = function() {
  if (_roti.old_onload) _roti.old_onload();
  var engine =  null;
  var gen = _roti.$("meta[name='generator']");
  var url = document.location.toString();

  if (gen && gen.length > 0) {
    var c = gen[0].content;
    if (c.search(/wordpress/i) >= 0) {
      engine = new RotiWordpress();
    } else if (c.search(/blogger/i) >= 0) {
      engine = new RotiBlogspot();
    } else if (c.search(/exteen/i) >= 0) {
      engine = new RotiExteen();
    }
  }

  if (engine == null && url.search(/exteen\.com/i) >= 0) {
    engine = new RotiExteen();
  }

  if (engine == null) {
    var script = _roti.$("script[type='text/javascript']");
    if (script) {
      for (var a=0,len=script.length; engine==null && a<len; a++) {
        if (script[a].src.search(/drupal\.js/) >= 0) {
          engine = new RotiDrupal();
        }
      }
    }
  }

  if (engine == null && document.getElementsByClassName('node').length > 0) {
    engine = new RotiDrupal();
  }

  //console.log("engine.name = ", engine.name); // FireBug

  engine.process();
}
